package br.unip.tcc.domotic.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import br.unip.tcc.domotic.bean.AbstractBean;
import br.unip.tcc.domotic.orm.AbstractORM;

public class ConvertUtils {
	
	public static AbstractORM convertBean2ORMWithRelationship(AbstractBean bean){
		AbstractORM ormInstance = null;
		
		try {
			if (bean != null){
				List<String> fieldsIn = new ArrayList<String>();
				// Find fieldsIn
				Field[] fields = bean.getClass().getDeclaredFields();
				for (Field field : fields){
					if (field.getType().getGenericSuperclass() != null && field.getType().getGenericSuperclass().equals(AbstractBean.class)){
						fieldsIn.add(field.getName());
					}
				}
				ormInstance = convertBean2ORM(bean, (String[]) fieldsIn.toArray(new String[]{}));
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		
		return ormInstance;
	}
	
	public static AbstractBean convertORM2BeanWithRelationship(AbstractORM orm){
		AbstractBean beanInstance = null;
		
		try {
			if (orm != null){
				List<String> fieldsIn = new ArrayList<String>();
				// Find fieldsIn
				Field[] fields = orm.getClass().getDeclaredFields();
				for (Field field : fields){
					if (field.getType().getGenericSuperclass() != null && field.getType().getGenericSuperclass().equals(AbstractORM.class)){
						fieldsIn.add(field.getName());
					}
				}
				beanInstance = convertORM2Bean(orm, (String[]) fieldsIn.toArray(new String[]{}));
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		
		return beanInstance;
	}
	
	
	
	public static AbstractORM convertBean2ORM(AbstractBean bean, String... fieldsIn){
		AbstractORM ormInstance = null;
		
		try {
			
			if (bean != null){
				ormInstance = convertBean2ORM(bean);
				
				List<Method> getMethods = retrieveGetMethods(bean.getClass());
				List<Method> setMethods = retrieveSetMethods(ormInstance.getClass());
				
				for (String fieldIn : fieldsIn){
					
					for (Method getMethod : getMethods){
						// If has the getMethod
						if (getMethod.getName().equalsIgnoreCase("get" + fieldIn)){
							Method setMethod = retrieveSetMethodFromGetMethod(setMethods, getMethod);
							if (setMethod != null){
								// If type is a AbstractBean and AbstractORM
								if (getMethod.getReturnType() != null && getMethod.getReturnType().getGenericSuperclass() != null && 
									getMethod.getReturnType().getGenericSuperclass().equals(AbstractBean.class) &&
									setMethod.getParameterTypes() != null && setMethod.getParameterTypes().length > 0 && 
									setMethod.getParameterTypes()[0].getGenericSuperclass().equals(AbstractORM.class)){
										// Convert and Set
										AbstractORM fieldInOrmInstance = convertBean2ORM((AbstractBean)getMethod.invoke(bean, new Object[]{}));
										setMethod.invoke(ormInstance,  new Object[]{fieldInOrmInstance});
								}
							}
						}
					}
				}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		return ormInstance;
	}
	
	public static AbstractBean convertORM2Bean(AbstractORM orm, String... fieldsIn){
		AbstractBean beanInstance = null;
		
		try {
			
			if (orm != null){
				beanInstance = convertORM2Bean(orm);
				
				List<Method> getMethods = retrieveGetMethods(orm.getClass());
				List<Method> setMethods = retrieveSetMethods(beanInstance.getClass());
				
				for (String fieldIn : fieldsIn){
					
					for (Method getMethod : getMethods){
						// If has the getMethod
						if (getMethod.getName().equalsIgnoreCase("get" + fieldIn)){
							Method setMethod = retrieveSetMethodFromGetMethod(setMethods, getMethod);
							if (setMethod != null){
								// If type is a AbstractBean and AbstractORM
								if (getMethod.getReturnType() != null && getMethod.getReturnType().getGenericSuperclass() != null && 
									getMethod.getReturnType().getGenericSuperclass().equals(AbstractORM.class) &&
									setMethod.getParameterTypes() != null && setMethod.getParameterTypes().length > 0 && 
									setMethod.getParameterTypes()[0].getGenericSuperclass().equals(AbstractBean.class)){
										// Convert and Set
										AbstractBean fieldInBeanInstance = convertORM2Bean((AbstractORM)getMethod.invoke(orm, new Object[]{}));
										setMethod.invoke(beanInstance,  new Object[]{fieldInBeanInstance});
								}
							}
						}
					}
				}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		return beanInstance;
	}
	
	public static AbstractORM convertBean2ORM(AbstractBean bean){
		AbstractORM ormInstance = null;
		
		try {
			if (bean != null){
				ormInstance = (AbstractORM) Class.forName("br.unip.tcc.domotic.orm." + bean.getClass().getSimpleName().replace("Bean", "")).newInstance();
				
				List<Method> getMethods = retrieveGetMethods(bean.getClass());
				List<Method> setMethods = retrieveSetMethods(ormInstance.getClass());
				
				for (Method setMethod : setMethods){
					Method getMethod = retrieveGetMethodFromSetMethod(getMethods, setMethod);
					// If has a correspondent getMethod
					if (getMethod != null){
						String returnClassType = getMethod.getReturnType().getName();
						String parameterClassType = setMethod.getParameterTypes()[0].getName();
						// If parameter and return has the same class
						if (returnClassType.equals(parameterClassType)){
							setMethod.invoke(ormInstance, getMethod.invoke(bean, new Object[]{}));
						}
					}
				}
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		return ormInstance;
	}
	
	public static AbstractBean convertORM2Bean(AbstractORM orm){
		AbstractBean beanInstance = null;
		
		try {
			if (orm != null){
				beanInstance = (AbstractBean) Class.forName("br.unip.tcc.domotic.bean." + orm.getClass().getSimpleName() + "Bean").newInstance();
				
				List<Method> getMethods = retrieveGetMethods(orm.getClass());
				List<Method> setMethods = retrieveSetMethods(beanInstance.getClass());
				
				for (Method setMethod : setMethods){
					Method getMethod = retrieveGetMethodFromSetMethod(getMethods, setMethod);
					// If has a correspondent getMethod
					if (getMethod != null){
						String returnClassType = getMethod.getReturnType().getName();
						String parameterClassType = setMethod.getParameterTypes()[0].getName();
						// If parameter and return has the same class
						if (returnClassType.equals(parameterClassType)){
							setMethod.invoke(beanInstance, getMethod.invoke(orm, new Object[]{}));
						}
					}
				}
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		return beanInstance;
	}
	
	public static List<Method> retrieveGetMethods(Class<?> clazz){
		return getMethodsStartsWith(clazz, "get");
	}
	
	public static List<Method> retrieveSetMethods(Class<?> clazz){
		return getMethodsStartsWith(clazz, "set");
	}
	
	public static List<Method> getMethodsStartsWith(Class<?> clazz, String startMethodName){
		List<Method> methods = new ArrayList<Method>();
		if (clazz != null && startMethodName != null){
			Method[] allMethods =  clazz.getMethods();
			for (Method method : allMethods){
				if (method.getName().startsWith(startMethodName)){
					methods.add(method);
				}
			}
		}
		return methods;
	}
	
	public static Method retrieveGetMethodFromSetMethod(List<Method> getMethods, Method setMethod){
		for (Method getMethod : getMethods){
			if (getMethod.getName().substring(1).equals(setMethod.getName().substring(1))){
				return getMethod;
			}
		}
		return null;
	}
	
	
	public static Method retrieveSetMethodFromGetMethod(List<Method> setMethods, Method getMethod){
		return retrieveGetMethodFromSetMethod(setMethods, getMethod);
	}
	
}